﻿/**************************************************************************
创建时间:   2013/7/1 
作    者:   张存
邮 	  箱:	zhangcunliang@126.com

Copyright (c) zhcun.cn

描	述： 图标操作类，验证码
记	录：
    增加剪切图片的实现 Cut() 2021.12.05
    NetStandard 与 Net40 统一实现 2022.3.19
***************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
#if NET40
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
#endif
#if NETSTANDARD
using System.DrawingCore;
using System.DrawingCore.Drawing2D;
using System.DrawingCore.Imaging;
#endif

namespace ASmile.Helpers
{
    public static class ImageHelper
    {
        #region 生成验证码

        /// <summary>
        /// 随即获取验证码
        /// </summary>
        /// <param name="width">放验证码图片控件的宽度</param>
        /// <param name="height">放验证码图片控件的高度</param>
        /// <param name="codeLength">验证码的长度</param>
        /// <param name="s">输出验证码的字符串</param>
        /// <returns></returns>
        public static Image GetVerifyCodeToImage(int width, int height, int codeLength, out string verifyCode)
        {
            Random ran = new Random(DateTime.Now.Millisecond);
            string strSource = "01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            verifyCode = "";
            for (int i = 0; i < codeLength; i++)
            {
                int r = ran.Next(0, strSource.Length);
                verifyCode += strSource[r].ToString();

            }
            Font font = new Font("Arial", 14, (FontStyle.Bold | FontStyle.Italic));
            Brush brush;
            Bitmap bitmap = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bitmap);
            SizeF totalSizeF = g.MeasureString(verifyCode, font);
            SizeF curCharSizeF;
            PointF startPointF = new PointF(0, (height - totalSizeF.Height) / 2);
            Random random = new Random(); //随机数产生器
            g.Clear(Color.White); //清空图片背景色  
            for (int i = 0; i < verifyCode.Length; i++)
            {
                brush = new LinearGradientBrush(new Point(0, 0), new Point(1, 1), Color.FromArgb(random.Next(255), random.Next(255), random.Next(255)), Color.FromArgb(random.Next(255), random.Next(255), random.Next(255)));
                g.DrawString(verifyCode[i].ToString(), font, brush, startPointF);
                curCharSizeF = g.MeasureString(verifyCode[i].ToString(), font);
                startPointF.X += curCharSizeF.Width;
            }

            //画图片的干扰线  
            for (int i = 0; i < 10; i++)
            {
                int x1 = random.Next(bitmap.Width);
                int x2 = random.Next(bitmap.Width);
                int y1 = random.Next(bitmap.Height);
                int y2 = random.Next(bitmap.Height);
                g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2);
            }

            //画图片的前景干扰点  
            for (int i = 0; i < 100; i++)
            {
                int x = random.Next(bitmap.Width);
                int y = random.Next(bitmap.Height);
                bitmap.SetPixel(x, y, Color.FromArgb(random.Next()));
            }

            g.DrawRectangle(new Pen(Color.Silver), 0, 0, bitmap.Width - 1, bitmap.Height - 1); //画图片的边框线  
            g.Dispose();

            return bitmap;

            #region 原实现已取消

            /*
            Bitmap bitmap = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bitmap);
            g.Clear(Color.White); //清除背景色

            int fontSize = height / 2; //字体的大小由画布的高决定
            // g.DrawRectangle(Pens.White, 0, 0, width, height);
            Font font = new Font("Arial", fontSize, FontStyle.Bold);
            //font.Bold = true;
            //验证码的位置
            PointF pf = new PointF();

            //写入验证码颜色的集合
            List<Brush> listBrush = new List<Brush>();
            listBrush.Add(Brushes.Red);
            listBrush.Add(Brushes.Gold);
            listBrush.Add(Brushes.Blue);
            listBrush.Add(Brushes.Firebrick);
            listBrush.Add(Brushes.GreenYellow);
            listBrush.Add(Brushes.Indigo);

            //加噪点
            Point pt1 = new Point();
            Point pt2 = new Point();
            for (int i = 0; i < 20; i++)
            {
                pt1.X = ran.Next(1, 3);
                pt1.Y = ran.Next(1, height);
                pt2.X = ran.Next(width - 3, width);
                pt2.Y = ran.Next(1, height);
                int brushIndex = ran.Next(0, listBrush.Count);
                g.DrawLine(Pens.Gray, pt1, pt2);
            }


            //按画布比例分配字符(一个字符写入画板的宽度)
            int oneLength = width / codeLength;
            if (fontSize >= oneLength)
            {
                fontSize = oneLength - 1;
            }
            for (int i = 0; i < s.Length; i++)
            {
                pf.X = ran.Next(i * oneLength + 1, (i + 1) * oneLength - fontSize);
                pf.Y = ran.Next(0, height - fontSize - 5);
                int brushIndex = ran.Next(0, listBrush.Count);
                g.DrawString(s[i].ToString(), font, listBrush[brushIndex], pf);
            }

            g.Dispose();
            //bitmap.RawFormat = 

            return bitmap;
            */
            #endregion
        }

        public static MemoryStream GetVerifyCodeToStream(int width, int height, int codeLength, out string s)
        {
            Image img = GetVerifyCodeToImage(width, height, codeLength, out s);
            MemoryStream stream = new MemoryStream();
            img.Save(stream, ImageFormat.Jpeg);
            return stream;
            //using (MemoryStream stream = new MemoryStream())
            //{
            //    img.Save(stream, ImageFormat.Jpeg);
            //    //输出图片流  
            //    return stream.ToArray();
            //}
        }
        public static byte[] GetVerifyCodeToBytes(int width, int height, int codeLength, out string s)
        {
            using (var stream = GetVerifyCodeToStream(width, height, codeLength, out s))
            {
                return stream.ToArray();
            }
        }

        #endregion

        #region 网上又找的生成验证码(未测试)（2019.7.18）

        //https://blog.csdn.net/ChaITSimpleLove/article/details/80531791

        /// <summary>
        /// 验证码图片 => Bitmap
        /// </summary>
        /// <param name="verifyCode">验证码</param>
        /// <param name="width">宽</param>
        /// <param name="height">高</param>
        /// <returns>Bitmap</returns>
        static Bitmap CreateBitmapByImgVerifyCode(string verifyCode, int width, int height)
        {
            Font font = new Font("Arial", 14, (FontStyle.Bold | FontStyle.Italic));
            Brush brush;
            Bitmap bitmap = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bitmap);
            SizeF totalSizeF = g.MeasureString(verifyCode, font);
            SizeF curCharSizeF;
            PointF startPointF = new PointF(0, (height - totalSizeF.Height) / 2);
            Random random = new Random(); //随机数产生器
            g.Clear(Color.White); //清空图片背景色  
            for (int i = 0; i < verifyCode.Length; i++)
            {
                brush = new LinearGradientBrush(new Point(0, 0), new Point(1, 1), Color.FromArgb(random.Next(255), random.Next(255), random.Next(255)), Color.FromArgb(random.Next(255), random.Next(255), random.Next(255)));
                g.DrawString(verifyCode[i].ToString(), font, brush, startPointF);
                curCharSizeF = g.MeasureString(verifyCode[i].ToString(), font);
                startPointF.X += curCharSizeF.Width;
            }

            //画图片的干扰线  
            for (int i = 0; i < 10; i++)
            {
                int x1 = random.Next(bitmap.Width);
                int x2 = random.Next(bitmap.Width);
                int y1 = random.Next(bitmap.Height);
                int y2 = random.Next(bitmap.Height);
                g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2);
            }

            //画图片的前景干扰点  
            for (int i = 0; i < 100; i++)
            {
                int x = random.Next(bitmap.Width);
                int y = random.Next(bitmap.Height);
                bitmap.SetPixel(x, y, Color.FromArgb(random.Next()));
            }

            g.DrawRectangle(new Pen(Color.Silver), 0, 0, bitmap.Width - 1, bitmap.Height - 1); //画图片的边框线  
            g.Dispose();
            return bitmap;
        }

        /// <summary>
        /// 验证码图片 => byte[]
        /// </summary>
        /// <param name="verifyCode">验证码</param>
        /// <param name="width">宽</param>
        /// <param name="height">高</param>
        /// <returns>byte[]</returns>
        static byte[] CreateByteByImgVerifyCode(string verifyCode, int width, int height)
        {
            Font font = new Font("Arial", 14, (FontStyle.Bold | FontStyle.Italic));
            Brush brush;
            Bitmap bitmap = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bitmap);
            SizeF totalSizeF = g.MeasureString(verifyCode, font);
            SizeF curCharSizeF;
            PointF startPointF = new PointF(0, (height - totalSizeF.Height) / 2);
            Random random = new Random(); //随机数产生器
            g.Clear(Color.White); //清空图片背景色  
            for (int i = 0; i < verifyCode.Length; i++)
            {
                brush = new LinearGradientBrush(new Point(0, 0), new Point(1, 1), Color.FromArgb(random.Next(255), random.Next(255), random.Next(255)), Color.FromArgb(random.Next(255), random.Next(255), random.Next(255)));
                g.DrawString(verifyCode[i].ToString(), font, brush, startPointF);
                curCharSizeF = g.MeasureString(verifyCode[i].ToString(), font);
                startPointF.X += curCharSizeF.Width;
            }

            //画图片的干扰线  
            for (int i = 0; i < 10; i++)
            {
                int x1 = random.Next(bitmap.Width);
                int x2 = random.Next(bitmap.Width);
                int y1 = random.Next(bitmap.Height);
                int y2 = random.Next(bitmap.Height);
                g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2);
            }

            //画图片的前景干扰点  
            for (int i = 0; i < 100; i++)
            {
                int x = random.Next(bitmap.Width);
                int y = random.Next(bitmap.Height);
                bitmap.SetPixel(x, y, Color.FromArgb(random.Next()));
            }

            g.DrawRectangle(new Pen(Color.Silver), 0, 0, bitmap.Width - 1, bitmap.Height - 1); //画图片的边框线  
            g.Dispose();

            //保存图片数据  
            MemoryStream stream = new MemoryStream();
            bitmap.Save(stream, ImageFormat.Jpeg);
            //输出图片流  
            return stream.ToArray();

        }

        #endregion

        #region 生成验证码 （数学运算）

        /// <summary>
        /// 获取数学计算的验证码
        /// </summary>
        /// <param name="codeResult">用于输出验证码结果值</param>
        /// <returns>返回验证码图片</returns>
        public static Bitmap GetCheckCodeByCalc(out int codeResult)
        {
            Random rd = new Random(); //创建随机数对象       
            //int codeResult = 0;
            int num1 = rd.Next(10); ;
            int num2 = rd.Next(10); ;
            string codeString = ""; //验证字符串

            int F = (rd.Next(3) + 1); //去掉整除的算法
            switch (F)
            {
                case 1:
                    codeResult = num1 + num2;
                    codeString = num1 + " 加上 " + num2;// +" 结果是" +Results;
                    break;
                case 2:
                    codeResult = num1 - num2;
                    codeString = num1 + " 减去 " + num2;
                    break;
                case 3:
                    codeResult = num1 * num2;
                    codeString = num1 + " 乘以 " + num2;
                    break;
                case 4:
                    if (num2 > 0)
                    {
                        codeResult = Convert.ToInt16(num1 / num2);
                        codeString = num1 + " 整除 " + num2;
                    }
                    else
                    {
                        codeResult = num1;
                        codeString = num1 + " 整除 1";// + Results;
                    }
                    break;
                default:
                    codeResult = 0;
                    break;
            }

            //验证码值存放到Session中用来比较
            //context.Session["ValidationCode_Sz"] = Results.ToString();

            string checkCode = codeString + " =？";//加上等于号

            int imgWidth = checkCode.Length * 12;
            int imgHeight = 22;

            Bitmap image = new Bitmap(imgWidth, imgHeight);
            {
                using (Graphics g = Graphics.FromImage(image))
                {
                    //生成随机生成器
                    Random random = new Random();
                    //清空图片背景色
                    g.Clear(Color.White);
                    //画图片的背景噪音线
                    int i;
                    for (i = 0; i < 25; i++)
                    {
                        int x1 = random.Next(image.Width);
                        int x2 = random.Next(image.Width);
                        int y1 = random.Next(image.Height);
                        int y2 = random.Next(image.Height);
                        g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2);
                    }
                    //定义一个含10种字体的数组
                    String[] fontFamily = { "Arial", "Verdana", "Comic Sans MS", "Impact", "Haettenschweiler", "幼圆", "黑体", "隶书", "宋体", "楷体_GB2312" };
                    FontStyle[] fontStyle = { FontStyle.Bold, FontStyle.Italic, FontStyle.Regular, FontStyle.Strikeout, FontStyle.Underline };
                    Font font = new Font(fontFamily[random.Next(9)], 12, fontStyle[random.Next(5)]);
                    LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height), Color.Blue, Color.DarkRed, 1.2F, true);
                    g.DrawString(checkCode, font, brush, 2, 2);

                    //画图片的前景噪音点
                    g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);
                    //System.IO.MemoryStream ms = new System.IO.MemoryStream();
                    //image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
                    return image;

                }
            }
        }

        #endregion

        #region 图片上加水印

        /// <summary>
        /// 向图片上写文字再输出
        /// </summary>
        public static Image WriteString(Image img, string text)
        {
            return WriteString(img, new PointF(), text);
        }
        /// <summary>
        /// 向图片上写文字再输出
        /// </summary>
        public static Image WriteString(Image img, PointF point, string text)
        {
            return WriteString(img, point, new Font("楷体", 20), Brushes.Black, text);
        }
        /// <summary>
        /// 向图片上写文字再输出
        /// </summary>
        public static Image WriteString(Image img, PointF point, Font font, string text)
        {
            return WriteString(img, point, font, Brushes.Black, text);
        }

        /// <summary>
        /// 向图片上写文字再输出
        /// </summary>
        public static Image WriteString(Image img, PointF point, Font font, Brush brush, string text)
        {
            Bitmap bmap = new Bitmap(img);

            using (Graphics g = Graphics.FromImage(bmap))
            {
                //g.DrawString(text, font, Brushes.Gold, point);
                g.DrawString(text, font, brush, point);
            }
            return bmap;

        }

        /// <summary>
        /// 将图片加载水印图片
        /// </summary>
        /// <param name="markImg">水印图片</param>
        /// <param name="fromImg">原图</param>
        /// <returns></returns>
        public static Image WriteImg(Image markImg, Image fromImg)
        {
            Bitmap b = new Bitmap(fromImg);
            using (Graphics g = Graphics.FromImage(b))
            {
                g.DrawImage(markImg, 0, 0, markImg.Width, markImg.Height);
                g.Dispose();
            }
            return b;
        }

        #endregion

        #region 缩略图

        /// <summary>
        /// 根据制定的高度和宽度改变图片大小
        /// </summary>
        /// <param name="img">图片源</param>
        /// <param name="width">宽度像素</param>
        /// <param name="heigth">高度像素</param>
        /// <returns></returns>
        public static Bitmap SmallBit(Image img, int width, int heigth)
        {
            Bitmap bitmap = new Bitmap(width, heigth); //要生成新图片的大小
            using (Graphics g = Graphics.FromImage(bitmap))
            {
                g.DrawImage(img, 0, 0, bitmap.Width, bitmap.Height);
            }
            return bitmap;
        }

        /// <summary>
        /// 根据指定高度改变图片大小
        /// </summary>
        /// <param name="img"></param>
        /// <param name="heigth"></param>
        /// <returns></returns>
        public static Image SmallBitByHeight(Image img, int heigth)
        {
            int h = heigth;
            //高度缩小的比例也是宽度缩小的比例
            // img.Height / heigth; 得到高度缩小的比例
            int w = (int)(img.Width / (img.Height / heigth));
            return SmallBit(img, w, h);
        }

        /// <summary>
        /// 根据指定宽度改变图片大小
        /// </summary>
        /// <param name="img"></param>
        /// <param name="width"></param>
        /// <returns></returns>
        public static Image SmallBitByWidth(Image img, int width)
        {
            int h = (int)(img.Height / (img.Width / width));
            return SmallBit(img, width, h);
        }

        /// <summary>
        /// 根据比例改变图片大小
        /// </summary>
        /// <param name="img"></param>
        /// <param name="ratio">比例大小如:0.5(百分之50)</param>
        /// <returns></returns>
        public static Image SmallBitByRatio(Image img, double ratio)
        {
            int w = (int)(img.Width * ratio);
            int h = (int)(img.Height * ratio);
            return SmallBit(img, w, h);
        }

        /// <summary>
        /// 旋转图片
        /// </summary>
        /// <param name="img"></param>
        /// <param name="rf">要旋转的图片类型(枚举)</param>
        /// <returns></returns>
        public static Image ReverseBit(Image img, RotateFlipType rf)
        {
            img.RotateFlip(rf);
            return img;
        }

        /// <summary>
        /// 根据画布大小比例得到图片
        /// </summary>
        /// <param name="img"></param>
        /// <param name="cWidth"></param>
        /// <param name="cHeight"></param>
        /// <returns></returns>
        public static Image SmallBitByCanva(Image img, int cWidth, int cHeight)
        {
            Bitmap bitmap = new Bitmap(cWidth, cHeight); //要生成新图片的大小

            //哪个缩小的比例大就以谁的比例缩小
            if (img.Width / cWidth > img.Height / cHeight)
            {
                return SmallBitByWidth(img, cWidth);

            }
            else
            {
                return SmallBitByHeight(img, cHeight);
            }
        }

        #endregion

        #region 图片翻转

        /// <summary>
        /// 图像翻转
        /// </summary>
        public static void Rotate(this Image img, RotateFlipType type)
        {
            img.RotateFlip(type);
        }
        /// <summary>
        /// 90度顺时针旋转
        /// </summary>
        public static Image Rotate90Flip(Image img)
        {
            img.RotateFlip(RotateFlipType.Rotate90FlipNone);
            return img;
        }

        /// <summary>
        /// 90度顺时针旋转,流转换
        /// </summary>
        public static byte[] Rotate90Flip(byte[] imgData)
        {
            using (var img = BytesToImage(imgData))
            {
                img.RotateFlip(RotateFlipType.Rotate90FlipNone);
                return img.ImageToBytes();
            }
        }

        /// <summary>
        /// 90度顺时针旋转,流转换
        /// </summary>
        public static byte[] Rotate90FlipData(byte[] imgData)
        {
            using (var img = BytesToImage(imgData))
            {
                img.RotateFlip(RotateFlipType.Rotate90FlipNone);
                return img.ImageToBytes();
            }
        }

        #endregion

        /// <summary>
        /// 剪切指定图片的范围
        /// </summary>
        /// <param name="img">要剪切的图片</param>
        /// <param name="x">要剪切的 x 坐标</param>
        /// <param name="y">要剪切的 y 坐标</param>
        /// <param name="iWidth">要剪切的 宽</param>
        /// <param name="iHeight">要剪切的 高</param>
        /// <returns>返回剪切后的图片</returns>
        public static Image Cut(Image img, int x, int y, int iWidth, int iHeight)
        {
            if (img == null) return null;
            int imgWidth = img.Width;
            int imgHeight = img.Height;
            if (x >= imgWidth || y >= imgHeight) return null;
            if (x + iWidth > imgWidth)
            {
                iWidth = imgWidth - x;
            }
            if (y + iHeight > imgHeight)
            {
                iHeight = imgHeight - y;
            }

            Bitmap bitmap = new Bitmap(iWidth, iHeight, PixelFormat.Format24bppRgb);
            bitmap.SetResolution(img.HorizontalResolution, img.VerticalResolution);
            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                graphics.Clear(Color.Transparent);
                graphics.CompositingQuality = CompositingQuality.HighQuality;
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.DrawImage(img, new Rectangle(0, 0, iWidth, iHeight), new Rectangle(x, y, iWidth, iHeight), GraphicsUnit.Pixel);
            }
            return bitmap;
        }
        /// <summary>
        /// 字节数组转换为图片
        /// </summary>
        public static Image BytesToImage(byte[] data)
        {
            if (data == null) return null;
            using (MemoryStream ms = new MemoryStream(data))
            {
                return Image.FromStream(ms);
            }
        }
        /// <summary>
        /// 将图片指定格式保存数据流
        /// </summary>
        public static byte[] ImageToBytes(this Image img, ImageFormat format)
        {
            using (img)
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    img.Save(ms, format);
                    return ms.ToArray();
                }
            }
        }
        /// <summary>
        /// 将图片默认保存为png格式的数据流
        /// </summary>
        public static byte[] ImageToBytes(this Image img)
        {
            return ImageToBytes(img, ImageFormat.Png);
        }
        /// <summary>
        /// 文件名读取图片流
        /// </summary>
        public static byte[] ToImageData(string fileName)
        {
            using (var img = Image.FromFile(fileName))
            {
                return img.ImageToBytes();
            }
        }

        public static void SaveImgFile(byte[] data, string fileName)
        {
            //创建一个bitmap类型的bmp变量来读取文件。
            Image bmp = BytesToImage(data);

            //新建第二个bitmap类型的bmp2变量，我这里是根据我的程序需要设置的。
            Image bmp2 = new Bitmap(bmp.Size.Width, bmp.Size.Height, PixelFormat.Format16bppRgb555);
            //将第一个bmp拷贝到bmp2中
            Graphics draw = Graphics.FromImage(bmp2);
            draw.DrawImage(bmp, 0, 0);
            draw.Dispose();
            bmp.Dispose();//释放bmp文件资源

            bmp2.Save(fileName);
        }

        /// <summary>
        /// 指定格式将Image转换为Bitmap
        /// </summary>
        /// <param name="img">image</param>
        /// <param name="format">格式</param>
        /// <returns>返回 Bitmap</returns>
        public static Bitmap ToBitmap(this Image img, ImageFormat format)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                img.Save(ms, format);
                return new Bitmap(new Bitmap(ms));
            }
        }
        /// <summary>
        /// Jpeg 格式转换为 Bitmap
        /// </summary>
        public static Bitmap ToBitmap(this Image img)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                img.Save(ms, ImageFormat.Jpeg);
                return new Bitmap(new Bitmap(ms));
            }
        }
    }
}